#!/usr/bin/python3
# -*- coding: utf-8 -*
from datetime import datetime  
from pathlib import Path
import random
import time
from flask import Flask, request, jsonify, send_from_directory, redirect
from jinja2 import Template
import os
import json
import shutil  

app = Flask(__name__)

# 允许上传的文件格式
ALLOWED_EXTENSIONS = {'bat', 'sh'}
# 访问本地文件位置
FOLDER = f"{os.path.dirname(os.path.abspath(__file__))}/static/upload"
URL_FOLDER = "/static/upload/"

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def safe_filename(filename):
    filename = filename.replace('/','_')
    filename = filename.replace('\\','_')
    filename = filename.replace(' ','_')
    return filename

class ImageUploader:
    def get_new_filename(self,path,filename,file_num = 0):
        if '.' in filename:
            change_name,file_type = filename.rsplit('.',1)
            change_name = change_name if file_num == 0 else f"{change_name}({file_num})"
            change_filename = f"{change_name}.{file_type}"
        else:
            change_filename = filename if file_num == 0 else f"{filename}({file_num})"

        file_path = os.path.join(path,change_filename)
        
        if os.path.exists(file_path):
            return self.get_new_filename(path,filename,file_num=file_num+1)

        return change_filename
    


    def upload(self,folder):
        if 'file' not in request.files:
            return jsonify({'error': 'No file part'}), 400
        file = request.files['file']
        if file.filename == '':
            return jsonify({'error': 'No selected file'}), 400
        
        folder = folder[1:] if folder.startswith('/') else folder
        folder = folder[:-1] if folder.endswith('/') else folder
        folder = folder.replace('../','')
        folder = folder.replace('..\\','')

        path = os.path.join(FOLDER,folder)
        
        if file and not allowed_file(file.filename):
            filename = safe_filename(file.filename)
            filename = self.get_new_filename(path,filename)
            file_path = os.path.join(path,filename)

            file.save(file_path)
            res = {'code': 200, 'message': '上传成功', 'data': {'filename':filename,'file_path':file_path}}
            print(f"callback : {json.dumps(res)}")
            return jsonify(res), 200
        else:
            res = {'code': '400', 'message': 'File type not allowed'}
            print(f"callback : {json.dumps(res)}")
            return jsonify(res), 400

@app.route('/favicon.ico')#设置icon
def favicon():
    return send_from_directory(os.path.join(app.root_path, 'static/.Template'),#对于当前文件所在路径,比如这里是static下的favicon.ico
    'favicon.ico', mimetype='image/vnd.microsoft.icon')

@app.route('/upload', methods=["GET", "POST"])
def upload_file():
    if request.method == "GET":
        print("callback : upload ")
        return """<head>  
                        <title>上传示例</title>  
                        <meta charset="UTF-8">  
                    </head>  
                    <body>  
                        <form action="" method="post" enctype="multipart/form-data">  
                            <input type="file" name="file">  
                            <input type="submit" value="上传">  
                        </form>  
                    </body>"""
    if request.method == "POST":
        folder = request.args.get("folder",default='/')

        uploader = ImageUploader()
        return uploader.upload(folder)


def sizeof_fmt(size):  
    power = 2**10  
    n = 0  
    power_labels = {0 : 'B', 1 : 'KB', 2 : 'MB', 3 : 'GB', 4 : 'TB', 5 : 'PB'}  
    while size > power:  
        size /= power  
        n += 1  
    return f"{size:.2f} {power_labels[n]}"  


def ls(path):
    files = []
    folders = []

    p = Path(path)  
    for item in p.iterdir():  
        if item.is_dir():  
            file_path = Path(os.path.join(path, item))  
            last_modified_time = file_path.stat().st_mtime 
            last_modified_readable = datetime.fromtimestamp(last_modified_time).strftime('%Y-%m-%d %H:%M:%S')
            # 如果需要递归列出所有子目录和文件，可以添加递归逻辑  
            folders.append([item.name,last_modified_readable,'-'])
        else:  
            
            last_modified_timestamp = os.path.getmtime(os.path.join(path, item))  
            last_modified_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(last_modified_timestamp))  
            file_path = Path(os.path.join(path, item))  
            file_size_bytes = file_path.stat().st_size  # 或者在某些Python版本中可能是 file_path.stat().size  

            file_type = ""
            if '.' in item.name:
                change_name,file_type = item.name.rsplit('.',1)

            files.append([item.name,last_modified_time,sizeof_fmt(file_size_bytes),file_type]) 
  
            
    return files, folders

def get_file_icon(filename):
    filetype = filename.split(".")[-1].lower()  
    
    icons = {
        'zip|7z|bz2|cab|gz|tar|rar':'archive.svg',
        'aac|aif|aifc|aiff|ape|au|flac|iff|m4a|mid|mp3|mpa|ra|wav|wma|f4a|f4b|oga|ogg|xm|it|s3m|mod':'audio.svg',
        'bin|hex':'bin.svg',
        'xml':'code.svg',
        'doc|docx|docm|dot|dotx|dotm|log|msg|odt|pages|rtf|tex|wpd|wps':'doc.svg',
        'bmp|png|tiff|tif|gif|jpg|jpeg|jpe|psd|ai|ico|webp':'img.svg',
        'xlsx|xlsm|xltx|xltm|xlam|xlr|xls|csv':'spreadsheet.svg',
        'ppt|pptx|pot|potx|pptm|potm|xps':'presentation.svg',
        'cpp':'c++.svg',
        'c':'c.svg',
        'css|sass|scss|less':'css3.svg',
        'ttf|TTF|woff|WOFF|woff2|WOFF2|otf|OTF':'font.svg',
        'h':'h.svg',
        'html|xhtml|shtml|htm|URL|url':'html5.svg',
        'nfo|info':'info.svg',
        'iso|img':'iso.svg',
        'jar|java':'java.svg',
        'js|json':'js.svg',
        'md':'markdown.svg',
        'pkg|dmg|rpm|deb':'package.svg',
        'pdf':'pdf.svg',
        'php|phtml':'php.svg',
        'py':'py.svg',
        'rb':'rb.svg',
        'bat|BAT|cmd|sh|ps|exe|EXE|msi|MSI':'script.svg',
        'sql':'sql.svg',
        'txt|cnf|conf|map|yaml':'text.svg',
        'svg|svgz':'vector.svg',
        'asf|asx|avi|flv|mkv|mov|mp4|mpg|rm|srt|swf|vob|wmv|m4v|f4v|f4p|ogv|webm':'video.svg',
        'default':'unknown.svg',
    }
    
    for k,v in icons.items():
        types = k.split("|")
        if filetype in types:
            return v
        
    return icons['default']

@app.route('/files')  
def to2_files():
    return files('/')
@app.route('/files/')  
def to_files():
    return files('/')
@app.route('/files/<path:subpath>')  

def files(subpath):
    subpath = subpath.replace('../','')
    subpath = subpath.replace('..\\','')
    subpath = subpath[1:] if subpath.startswith('/') else subpath
    subpath = subpath[:-1] if subpath.endswith('/') else subpath
    
    folder = os.path.join(FOLDER,subpath)
    if not os.path.isdir(folder):
        return redirect('/files')
    
    if subpath == "":
        father_folder = "/"
    else:
        father_folder = "/files/" + subpath
        father_folder = father_folder.rsplit('/',1)[0]+'/'
    

    files, folders = ls(folder)
    files = sorted(files, key=lambda x: x[0])  
    folders = sorted(folders, key=lambda x: x[0])  
    
    lists = []
    if subpath == "":
        urlpath = ""
    else:
        urlpath = subpath+"/"
    for i in folders:
        lists.append({'name':i[0],'url':"/files/"+urlpath+i[0],'icon':f"folder.svg",'last_update_time':i[1],'size':i[2]})
    for i in files:
        if i[3] in ['txt','log','md',
                    'js','html','css',
                    'py',
                    ]:
            url="/edit/"+urlpath+i[0]
        else:
            url= URL_FOLDER+urlpath+i[0]
        lists.append({'name':i[0],'url':url,'icon':get_file_icon(i[0]),'last_update_time':i[1],'size':i[2],'file_type':i[3]})

    # 获取模板文件
    with open(f"assets/files.html",'r', encoding='utf-8') as f:
        txt = f.read()
    
    # 实例化模板
    tmpl = Template(txt)
    config = {"path":
              "/" if subpath == "" else f"/{subpath}/",
              
              "lists" : lists,'father_folder':father_folder}
    result = tmpl.render(config)
    
    return result

@app.route("/mkdir", methods=["POST"])
def mkdir():
    path = request.form.get('path','/')
    folder = request.form.get('folder','new_folder')
    type = request.form.get('type','folder')
    folder = safe_filename(folder)

    path = path.replace('../','')
    path = path.replace('..\\','')
    path = path[1:] if path.startswith('/') else path
    path = path[:-1] if path.endswith('/') else path
    folder_path = os.path.join(FOLDER,path,folder)
    if not os.path.exists(folder_path):
        try:
            if type == "folder":
                os.makedirs(folder_path)
            else:
                with open(folder_path, 'w', encoding='utf-8') as file:
                    file.write('')
            return jsonify({'code': 200, 'message': '创建成功'}), 200
        except :
            return jsonify({'code': 400, 'message': '创建文件夹失败'}), 200
    else:
        return jsonify({'code': 400, 'message': '文件或文件夹已存在'}), 200


@app.route("/delete", methods=["POST"])
def delete():
    path = request.form.get('path','/')

    path = path.replace('../','')
    path = path.replace('..\\','')
    path = path[1:] if path.startswith('/') else path
    path = path[:-1] if path.endswith('/') else path
    folder_path = os.path.join(FOLDER,path)
    if os.path.exists(folder_path):
        try:
            if os.path.isdir(folder_path):  
                shutil.rmtree(folder_path)  
            else:
                os.remove(folder_path)  

            return jsonify({'code': 200, 'message': '删除成功'}), 200
        except :
            return jsonify({'code': 400, 'message': '删除失败'}), 200
    else:
        return jsonify({'code': 400, 'message': '文件或文件夹不存在'}), 200



@app.route('/edit')  
def to2_edit():
    return edit('/new.txt')
@app.route('/edit/')  
def to_edit():
    return edit('/new.txt')
@app.route("/edit/<path:subpath>", methods=["GET", "POST"])
def edit(subpath):
    print(subpath)
    subpath = subpath.replace('../','')
    subpath = subpath.replace('..\\','')
    subpath = subpath[1:] if subpath.startswith('/') else subpath
    subpath = subpath[:-1] if subpath.endswith('/') else subpath
    folder = os.path.join(FOLDER,subpath)

    if request.method == "GET":
        
        file_data = ''
        if not os.path.isfile(folder):
            return redirect('../')
        try:
            with open(folder,'r', encoding='utf-8') as f:
                file_data = f.read()
        except:
            return redirect('../')
        # 获取模板文件
        with open(f"assets/notepad.html",'r', encoding='utf-8') as f:
            txt = f.read()

        # 实例化模板
        tmpl = Template(txt)
        config = {"path":subpath,"download":URL_FOLDER+subpath,'file_data':file_data}
        result = tmpl.render(config)
        
        return result
    
    if request.method == "POST":
        res = {}
        value = request.form.get("value",default='')


        if os.path.isfile(folder):
            try:
                with open(folder,'w', encoding='utf-8') as f:
                    file_data = f.write(value)
                
                return jsonify({'code': 200, 'message': '修改成功'}), 200
            except :
                return jsonify({'code': 400, 'message': '修改失败'}), 200
        else:
            return jsonify({'code': 400, 'message': '文件不存在'}), 200
        

        return jsonify(res), 200
    
    

@app.route("/", methods=["GET", "POST"])
def process():
    if request.method == "GET":
    # 获取模板文件
        with open(f"assets/index.html",'r', encoding='utf-8') as f:
            txt = f.read()
        print("callback : 主页 ")
        return txt
    if request.method == "POST":
        res = {}
        for i in list(request.form):
            res[i] = request.form.get(i)
        print(f"callback : {json.dumps(res)}")
        return jsonify(res), 200
    
    
if __name__ == '__main__':
    app.config['JSON_AS_ASCII'] = False 
    app.config['STATIC_FOLDER'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')


    #app.run(debug=True)
    app.run(host='0.0.0.0', port=5000,use_reloader=False, use_debugger=False)
